package com.changpei.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.changpei.common.cache.ContentCache;
import com.changpei.common.cache.UserCache;
import com.changpei.common.utils.FileUtil;
import com.changpei.common.utils.JsonUtil;
import com.changpei.core.config.AliYunConfig;
import com.changpei.core.config.AliYunOssUtil;
import com.changpei.core.config.CoreConfig;
import com.changpei.core.config.RabbitInfoConfig;
import com.changpei.core.dao.UserMapper;
import com.changpei.core.pojo.Content;
import com.changpei.core.pojo.User;
import com.changpei.core.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * <p>Title: User</p>
 * <p>Description: User业务层接口实现类</p>
 *
 * @author weibiao
 */
@Service
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private ContentCache contentCache;

    @Autowired
    private RabbitInfoConfig rabbitInfoConfig;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private UserCache userCache;

    @Autowired
    private AliYunOssUtil aliYunOssUtil;

    @Autowired
    private AliYunConfig aliYunConfig;

    @Autowired
    private CoreConfig coreConfig;

    /**
     * 锁对象
     */
    private final Lock lock = new ReentrantLock();

    @Override
    public JSONObject ops(Map<String, Object> param) {
        JSONObject r = new JSONObject();
        // 加锁
        lock.lock();
        try {
            String token = param.get("token").toString();
            long contentId = Long.parseLong(param.get("contentId").toString());
            String cacheKey = param.get("cacheKey").toString();
            String cacheCountKey = cacheKey + "_count";

            // 用户行为收集标识
            // 由于微信取消了分享回调函数，所以不记录分享记录数
            String[] identify = {"like", "collect", "share"};

            Map<Boolean, Long> map = new HashMap<>(1);

            boolean isSigned = contentCache.ops(token, contentId, cacheKey);

            // 记录用户获赞数
            if (cacheKey.contains(identify[0])) {
                long userId = Long.parseLong(param.get("userId").toString());
                long wonLikedNum;
                if (isSigned) {
                    wonLikedNum = userCache.incrWinLike(userId);
                } else {
                    wonLikedNum = userCache.decrWinLike(userId);
                }

                JSONObject jsonObject = new JSONObject();
                jsonObject.put("id", userId);
                jsonObject.put("wonLikedNum", wonLikedNum);

                // 发送到rabbitMQ,通知es修改信息
                rabbitTemplate.convertAndSend(rabbitInfoConfig.getExchange().get("userModify"),
                        rabbitInfoConfig.getRouting().get("userModify"),
                        JSON.toJSONString(jsonObject, SerializerFeature.WriteMapNullValue,
                                SerializerFeature.WriteNullNumberAsZero));
            }

            // 更新相关操作数量
            if (isSigned) {
                map.putAll(contentCache.incrCount(contentId, cacheCountKey));
            } else {
                map.putAll(contentCache.derCount(contentId, cacheCountKey));
            }

            // 发送有效数据
            if (map.containsKey(true)) {
                Content content = new Content();
                content.setId(contentId);

                // 利用反射赋值
                for (Field field : content.getClass().getDeclaredFields()) {
                    // 开启私有属性访问权限
                    field.setAccessible(true);
                    String fieldName = field.getName();
                    for (String id : identify) {
                        if (fieldName.contains(id) && cacheCountKey.contains(id)) {
                            field.set(content, map.get(true));
                        }
                    }
                }

                String msg = JSON.toJSONString(content);
                JSONObject result = JSONObject.parseObject(msg);
                result.put("result", isSigned);

                // 发送到rabbitMQ,通知es修改信息
                rabbitTemplate.convertAndSend(rabbitInfoConfig.getExchange().get("content"), rabbitInfoConfig.getRouting().get("content"), msg);

                return result;
            }

            r.put("result", false);
            return r;
        } catch (Exception e) {
            r.put("result", false);
            e.printStackTrace();
            log.error("用户点赞异常:{}", e.getMessage());
            return r;
        } finally {
            // 释放锁
            lock.unlock();
        }
    }

    @Override
    public boolean operated(Map<String, Object> param) {
        String token = param.get("token").toString();
        long contentId = Long.parseLong(param.get("contentId").toString());
        String cacheKey = param.get("cacheKey").toString();
        Long userId = userCache.getUserId(token);
        return !ObjectUtils.isEmpty(contentCache.hasHistory(userId, contentId, cacheKey));
    }

    @Override
    public JSONObject modify(User user) {
        JSONObject result = new JSONObject();
        result.put("result", false);
        result.put("msg", "用户修改信息失败");
        // 校验返回中的key
        String r = "r";
        String m = "m";
        // 加锁
        lock.lock();
        try {
            if (StringUtils.isNotEmpty(user.getDescription().trim()) || StringUtils.isNotEmpty(user.getNickName().trim())) {
                JSONObject checkUserInfo = checkUserInfo(user);
                if (!checkUserInfo.getBoolean(r)) {
                    result.put("msg", checkUserInfo.getString(m));
                    return result;
                }
                boolean b = this.updateById(user);
                if (b) {
                    // 准备同步数据
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("id", user.getId());
                    jsonObject.put("nickName", user.getNickName());
                    jsonObject.put("description", user.getDescription());
                    // 同步数据
                    synUserInfo(jsonObject);
                    result.put("result", true);
                    result.put("msg", "操作成功");
                    return result;
                }
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("用户修改信息失败", e.getMessage());
            return result;
        } finally {
            // 释放锁
            lock.unlock();
        }
    }

    /**
     * 同步数据
     * @param user
     */
    private void synUserInfo(JSONObject jsonObject) {
        // 发送到rabbitMQ,通知es修改信息
        rabbitTemplate.convertAndSend(rabbitInfoConfig.getExchange().get("userModify"),
                rabbitInfoConfig.getRouting().get("userModify"),
                JSON.toJSONString(jsonObject, SerializerFeature.WriteMapNullValue,
                        SerializerFeature.WriteNullNumberAsZero));
    }

    /**
     * 校验用户信息
     * @param user
     * @return
     * @throws Exception
     */
    private JSONObject checkUserInfo(User user) throws Exception {
        JSONObject result = new JSONObject();
        result.put("r", true);
        result.put("m", "操作成功");
        if (StringUtils.isNotEmpty(user.getNickName())) {
            boolean nickNameResult = aliYunOssUtil.scanText(user.getNickName());
            if (!nickNameResult) {
                result.put("r", false);
                result.put("m", "您的昵称涉及敏感内容，请修改～");
                return result;
            }
        }

        if (StringUtils.isNotEmpty(user.getDescription())) {
            boolean descriptionResult = aliYunOssUtil.scanText(user.getDescription());
            if (!descriptionResult) {
                result.put("r", false);
                result.put("m", "您的个人介绍涉及敏感内容，请修改～");
                return result;
            }
        }

        return result;
    }

    @Override
    public boolean follow(Map<String, Object> param) {
        // 加锁
        lock.lock();
        try {
            String token = param.get("token").toString();
            long followId = Long.parseLong(param.get("followId").toString());

            // 操作类型，true代表取消关注，false代表关注
            boolean type = Boolean.parseBoolean(param.get("type").toString());

            List<JSONObject> list = new ArrayList<>();

            long userId = userCache.getUserId(token);

            // 关注 || 取消关注
            boolean follow = type ? userCache.unfollow(token, followId) : userCache.follow(token, followId);

            if (follow) {
                // 获取关注数
                int followedNum = userCache.followers(token).size();
                JSONObject followMsg = new JSONObject();
                followMsg.put("id", userId);
                followMsg.put("followedNum", followedNum);

                // 获取粉丝数
                int fansNum = userCache.fans(followId).size();
                JSONObject fansMsg = new JSONObject();
                fansMsg.put("id", followId);
                fansMsg.put("fansNum", fansNum);

                // 添加消息体
                list.add(followMsg);
                list.add(fansMsg);

                // 发送到rabbitMQ,通知es修改信息
                rabbitTemplate.convertAndSend(rabbitInfoConfig.getExchange().get("follow"), rabbitInfoConfig.getRouting().get("follow"), JSON.toJSONString(list));
            }

            return follow;
        } catch (Exception e) {
            log.error("用户关注异常:{}", e.getMessage());
            return false;
        } finally {
            // 释放锁
            lock.unlock();
        }
    }

    @Override
    public boolean checkRepeatName(String nickName) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("nick_name", nickName);
        User one = this.getOne(queryWrapper);
        return ObjectUtils.isEmpty(one);
    }

    @Override
    public JSONObject modifyWithFile(MultipartFile file, String user, String type) {
        JSONObject result = new JSONObject();
        result.put("result", false);
        result.put("msg", "用户修改信息失败");

        User bean = JsonUtil.jsonToBean(JSONObject.parseObject(user), User.class);
        String modifyType = "avatar";
        String avatarContents = "face";
        String coveredContents = "covered";
        if (ObjectUtils.isEmpty(file)) {
            return result;
        }

        // 加锁
        lock.lock();
        try {
            // 文件下载
            String fileName = FileUtil.renameToUuid(Objects.requireNonNull(file.getOriginalFilename()));
            FileUtil.uploadFile(file.getBytes(), coreConfig.getUploadPath(), fileName);

            if (!isValid(fileName)) {
                FileUtil.deleteFile(coreConfig.getUploadPath() + "/" + fileName);
                result.put("msg", "文件要以.jpeg、.jpg、.png、.bmp结尾，请修改～");
                return result;
            }

            if (modifyType.equals(type)) {
                // 修改头像
                String eTag = aliYunOssUtil.upload(fileName, avatarContents);
                if (StringUtils.isNotEmpty(eTag)) {
                    String checkUrl = aliYunConfig.getFilePrefix() + avatarContents + "/" + fileName;
                    boolean b = aliYunOssUtil.scanImage(checkUrl);
                    if (!b) {
                        // 删除本地下载文件
                        FileUtil.deleteFile(coreConfig.getUploadPath() + "/" + fileName);
                        result.put("msg", "头像图片涉及敏感内容，请修改～");
                        return result;
                    }

                    // 修改用户名密码
                    modify(bean);
                    // 同步备份库中
                    bean.setAvatarUrl(fileName);
                    boolean u = this.updateById(bean);
                    if (u) {
                        JSONObject o = new JSONObject();
                        // 同步信息
                        o.put("id", bean.getId());
                        o.put("avatarUrl", fileName);
                        synUserInfo(o);
                    }
                }
            } else {
                String eTag = aliYunOssUtil.upload(fileName, coveredContents);
                if (StringUtils.isNotEmpty(eTag)) {
                    String checkUrl = aliYunConfig.getFilePrefix() + coveredContents + "/" + fileName;
                    boolean b = aliYunOssUtil.scanImage(checkUrl);
                    if (!b) {
                        // 删除本地下载文件
                        FileUtil.deleteFile(coreConfig.getUploadPath() + "/" + fileName);
                        result.put("msg", "背景图片涉及敏感内容，请修改～");
                        return result;
                    }

                    // 同步备份库中
                    bean.setCoveredImg(fileName);
                    boolean u = this.updateById(bean);
                    if (u) {
                        JSONObject o = new JSONObject();
                        // 同步信息
                        o.put("id", bean.getId());
                        o.put("coveredImg", fileName);
                        synUserInfo(o);
                    }
                }
            }

            // 删除本地下载文件
            FileUtil.deleteFile(coreConfig.getUploadPath() + "/" + fileName);
            result.put("result", true);
            result.put("msg", "操作成功");
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("用户修改信息失败", e.getMessage());
            return result;
        } finally {
            // 释放锁
            lock.unlock();
        }
    }

    @SuppressWarnings("all")
    private boolean isValid(String fileName) {
        if (fileName.toUpperCase().endsWith("JPEG")
                || fileName.toUpperCase().endsWith("JPG")
                || fileName.toUpperCase().endsWith("PNG")
                || fileName.toUpperCase().endsWith("BMP")) {
            return true;
        }
        return false;
    }

}
